package com.jdrx.sw.sewage.service.common;

import com.jdrx.platform.commons.rest.utils.JsonMapper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by qingsheduan on 2017/7/26.
 */
@Service
public class RedisService {

    private Logger logger = LoggerFactory.getLogger(RedisService.class);

    @Autowired
    RedisTemplate<String, String> redisTemplate;


    /**
     * 删除缓存<br>
     * 根据key精确匹配删除
     *
     * @param key
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }


    /**
     * 批量删除缓存数据
     *
     * @param keys
     */
    public void dels(List<String> keys) {
        // 执行批量删除操作时先序列化template
        redisTemplate.setKeySerializer(new JdkSerializationRedisSerializer());
        redisTemplate.delete(keys);
    }

    /**
     * 批量删除<br>
     * （该操作会执行模糊查询，请尽量不要使用，以免影响性能或误删）
     *
     * @param pattern
     */
    public void batchDel(String... pattern) {
        for (String kp : pattern) {
            redisTemplate.delete(redisTemplate.keys(kp + "*"));
        }
    }

    /**
     * redis-increment
     *
     * @param key
     * @return
     */
    public void increment(String key, Long value) {
        redisTemplate.opsForValue().increment(key, value);
    }

    /**
     * 取得缓存（int型）
     *
     * @param key
     * @return
     */
    public Integer getInt(String key) {
        String value = redisTemplate.boundValueOps(key).get();
        if (StringUtils.isNotBlank(value)) {
            return Integer.valueOf(value);
        }
        return null;
    }

    /**
     * 取得缓存（字符串类型）
     *
     * @param key
     * @return
     */
    public String getStr(String key) {
        return redisTemplate.boundValueOps(key).get();
    }

    /**
     * 取得缓存（字符串类型）
     *
     * @param key
     * @return
     */
    public String getStr(String key, boolean retain) {
        String value = redisTemplate.boundValueOps(key).get();
        if (!retain) {
            redisTemplate.delete(key);
        }
        return value;
    }

    /**
     * 获取缓存<br>
     * 注：基本数据类型(Character除外)，请直接使用get(String key, Class<T> clazz)取值
     *
     * @param key
     * @return
     */
    public Object getObj(String key) {
        return redisTemplate.boundValueOps(key).get();
    }

    /**
     * 获取缓存<br>
     * 注：java 8种基本类型的数据请直接使用get(String key, Class<T> clazz)取值
     *
     * @param key
     * @param retain 是否保留
     * @return
     */
    public Object getObj(String key, boolean retain) {
        Object obj = redisTemplate.boundValueOps(key).get();
        if (!retain) {
            redisTemplate.delete(key);
        }
        return obj;
    }

    /**
     * 获取缓存<br>
     * 注：该方法暂不支持Character数据类型
     *
     * @param key   key
     * @param clazz 类型
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> clazz) {
        return (T) redisTemplate.boundValueOps(key).get();
    }

    /**
     * 获取缓存json对象<br>
     *
     * @param key   key
     * @param clazz 类型
     * @return
     */
    public <T> T getJson(String key, Class<T> clazz) {
        return (T) JsonMapper.fromJsonString(redisTemplate.boundValueOps(key).get(), clazz);
    }

    /**
     * 将value对象写入缓存
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public void set(String key, Object value, Long time) {
        if (value.getClass().equals(String.class)) {
            redisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Integer.class)) {
            redisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Double.class)) {
            redisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Float.class)) {
            redisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Short.class)) {
            redisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Long.class)) {
            redisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Boolean.class)) {
            redisTemplate.opsForValue().set(key, value.toString());
        } else {
            redisTemplate.opsForValue().set(key, value.toString());
        }
        if (Objects.nonNull(time) && time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 将value对象以JSON格式写入缓存
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public void setJson(String key, Object value, Long time) {
        redisTemplate.opsForValue().set(key, JsonMapper.toJsonString(value));
        if (Objects.nonNull(time) && time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 将value对象以JSON格式写入缓存
     *
     * @param key
     * @param value
     */
    public void setJson(String key, Object value) {
        setJson(key, value, null);
    }

    /**
     * 更新key对象field的值
     *
     * @param key   缓存key
     * @param field 缓存对象field
     * @param value 缓存对象field值
    public void setJsonField(String key, String field, String value) {
    JSONObject obj = JSON.parseObject(redisTemplate.boundValueOps(key).get());
    obj.put(field, value);
    redisTemplate.opsForValue().set(key, obj.toJSONString());
    }*/


    /**
     * 递减操作
     *
     * @param key
     * @param by
     * @return
     */
    public double decr(String key, double by) {
        return redisTemplate.opsForValue().increment(key, -by);
    }

    /**
     * 递增操作
     *
     * @param key
     * @param by
     * @return
     */
    public double incr(String key, double by) {
        return redisTemplate.opsForValue().increment(key, by);
    }

    /**
     * 获取double类型值
     *
     * @param key
     * @return
     */
    public double getDouble(String key) {
        String value = redisTemplate.boundValueOps(key).get();
        if (StringUtils.isNotBlank(value)) {
            return Double.valueOf(value);
        }
        return 0d;
    }

    /**
     * 设置double类型值
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public void setDouble(String key, double value, Long time) {
        redisTemplate.opsForValue().set(key, String.valueOf(value));
        if (Objects.nonNull(time) && time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 设置double类型值
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public void setInt(String key, int value, Long time) {
        redisTemplate.opsForValue().set(key, String.valueOf(value));
        if (Objects.nonNull(time) && time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 将map写入缓存
     *
     * @param key
     * @param map
     * @param time 失效时间(秒)
     */
    public <T> void setMap(String key, Map<String, T> map, Long time) {
        redisTemplate.opsForHash().putAll(key, map);
        if (time != null) {
            expire(key, time);
        }
    }

    /**
     * 将set写入缓存
     *
     * @param key
     * @param map
     * @param time
     * @param <T>
     */
    public <T> void setSet(String key, String[] map, Long time) {
        redisTemplate.opsForSet().add(key, map);
        expire(key, time);
    }

    /**
     * @param key
     * @param list
     */
    public void setList(String key, List<String> list) {
        redisTemplate.delete(key);
        redisTemplate.opsForList().leftPushAll(key, list);
    }

    /**
     * 返回List
     *
     * @param hashKey
     * @return
     */
    public List<String> getList(String hashKey) {
        return redisTemplate.opsForList().range(hashKey, 0, -1);
    }

    /**
     * 将map写入缓存
     *
     * @param key
     * @param map
     * @param time 失效时间(秒)
     @SuppressWarnings("unchecked") public <T> void setMap(String key, T obj, Long time) {
     Map<String, String> map = (Map<String, String>) JsonMapper.parseObject(obj, Map.class);
     redisTemplate.opsForHash().putAll(key, map);
     }*/


    /**
     * 向key对应的map中添加缓存对象
     *
     * @param key
     * @param map
     */
    public <T> void addMap(String key, Map<String, T> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 向key对应的map中添加缓存对象
     *
     * @param key   cache对象key
     * @param field map对应的key
     * @param value 值
     */
    public void addMap(String key, String field, String value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * 向key对应的map中添加缓存对象
     *
     * @param key   cache对象key
     * @param field map对应的key
     * @param obj   对象
     */
    public <T> void addMap(String key, String field, T obj) {
        redisTemplate.opsForHash().put(key, field, obj);
    }

    /**
     * 获取map缓存
     *
     * @param key
     * @param clazz
     * @return
     */
    public <T> Map<String, T> mget(String key, Class<T> clazz) {
        BoundHashOperations<String, String, T> boundHashOperations = redisTemplate.boundHashOps(key);
        return boundHashOperations.entries();
    }

    /**
     * 获取map缓存
     *
     * @param key
     * @param clazz
     * @return
     */
    public <T> T getMap(String key, Class<T> clazz) {
        BoundHashOperations<String, String, String> boundHashOperations = redisTemplate.boundHashOps(key);
        Map<String, String> map = boundHashOperations.entries();
        if (map == null || map.size() == 0) {
            return null;
        }
        String jsonString = JsonMapper.toJsonString(map);
        return (T) JsonMapper.fromJsonString(jsonString, clazz);
    }

    public Map getMap(String key) {
        BoundHashOperations<String, String, String> boundHashOperations = redisTemplate.boundHashOps(key);
        Map<String, String> map = boundHashOperations.entries();
        if (map == null || map.size() == 0) {
            return null;
        }
        return map;
    }

    /**
     * 批量获取Map缓存
     *
     * @param keys
     * @param clazz
     * @return
     */
    public List<Object> getMapPipeline(List<String> keys, Class clazz) {
        List<Object> objects = redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
                for (String key : keys) {
                    Map<String, String> map = stringRedisConn.hGetAll(key);
                }
                return null;
            }
        });
        return objects;
    }


    /**
     * @param key
     * @return
     */
    public Map getMapLiteral(String key) {
        BoundHashOperations<String, String, String> boundHashOperations = redisTemplate.boundHashOps(key);
        Map<String, String> map = boundHashOperations.entries();
        return map;
    }

    /**
     * 获取map缓存中的某个对象
     *
     * @param key
     * @param field
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T getMapField(String key, String field) {
        return (T) redisTemplate.boundHashOps(key).get(field);
    }

    /**
     * 删除map中的某个对象
     *
     * @param key   map对应的key
     * @param field map中该对象的key
     * @author lh
     * @date 2016年8月10日
     */
    public void delMapField(String key, String... field) {
        BoundHashOperations<String, String, ?> boundHashOperations = redisTemplate.boundHashOps(key);
        boundHashOperations.delete(field);
    }

    /**
     * 指定缓存的失效时间
     *
     * @param key  缓存KEY
     * @param time 失效时间(秒)
     * @author FangJun
     * @date 2016年8月14日
     */
    public void expire(String key, Long time) {
        if (Objects.nonNull(time) && time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 添加set
     *
     * @param key
     * @param value
     */
    public void sadd(String key, String... value) {
        redisTemplate.boundSetOps(key).add(value);
    }

    /**
     * 删除set集合中的对象
     *
     * @param key
     * @param value
     */
    public void srem(String key, String... value) {
        redisTemplate.boundSetOps(key).remove(value);
    }

    /**
     * set重命名
     *
     * @param oldkey
     * @param newkey
     */
    public void srename(String oldkey, String newkey) {
        redisTemplate.boundSetOps(oldkey).rename(newkey);
    }

    /**
     * 短信缓存
     *
     * @param key
     * @param value
     * @param time
     * @author fxl
     * @date 2016年9月11日
     */
    public void setIntForPhone(String key, Object value, int time) {
        redisTemplate.opsForValue().set(key, JsonMapper.toJsonString(value));
        if (time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 模糊查询keys
     *
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }


    /**
     * 设置redis List查询
     */
    public void rPushList(String key, String value) {
        redisTemplate.boundListOps(key).rightPush(value);
    }

    /**
     * 获取队列中所有的信息
     *
     * @return
     */
    public List<String> lrangeList(String key) {
        return redisTemplate.boundListOps(key).range(0, -1);
    }

    /**
     * 判断key值是否存在
     *
     * @param key
     * @return
     */
    public Boolean existsKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 存储值至zset
     *
     * @param zsetKey
     * @param zsetK
     * @param zScore
     * @return
     */
    public Boolean zsetAdd(String zsetKey, String zsetK, Double zScore) {
        return redisTemplate.opsForZSet().add(zsetKey, zsetK, zScore);
    }

    /**
     * Zset增加成员分数
     *
     * @param zsetKey
     * @param zsetK
     * @param zScore
     * @return
     */
    public Double zsetIncrby(String zsetKey, String zsetK, Double zScore) {
        return redisTemplate.opsForZSet().incrementScore(zsetKey, zsetK, zScore);
    }


    /**
     * 根据rank获取zset中存储项
     *
     * @param zsetKey
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<String>> zsetGetWithRange(String zsetKey, Long start, Long end) {
        return redisTemplate.opsForZSet().rangeWithScores(zsetKey, start, end);
    }

    /**
     * 根据分数获取zset中(倒序)
     *
     * @param zsetKey
     * @param start
     * @param end
     * @return
     */
    public Set<String> zsetKeyGetWithRange(String zsetKey, Long start, Long end) {
        return redisTemplate.opsForZSet().reverseRange(zsetKey, start, end);
    }

    /**
     * 获取zset对应的score
     *
     * @param zsetKey
     * @param zK
     * @return
     */
    public Double zsetGetScoreWithKey(String zsetKey, String zK) {
        return redisTemplate.opsForZSet().score(zsetKey, zK);
    }

    private void pipelined() {
        redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
                for (int i = 0; i < 0; i++) {
                    stringRedisConn.rPop("myqueue");
                }
                return null;
            }
        });
    }

    /**
     * 指定缓存失效时间
     *
     * @param key 缓存key
     * @param date 失效时间
     */
    public void expireAt(String key, Date date){
        if (Objects.nonNull(date)) {
            redisTemplate.expireAt(key, date);
        }
    }

    /**
     * 根据key的list批量查找redis数据
     * @param keys 需要查找的key的集合
     * @return
     */
    public List<Object> batchQueryByKeys(List<String> keys){
        if(null == keys || keys.size() == 0 ){
            return null;
        }

        List<Object> results = redisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Map<String, Object> doInRedis(RedisConnection redisConnection) throws DataAccessException {
                for (String key : keys) {
                    redisConnection.hGetAll(key.getBytes());
                }
                return null;
            }
        });
        return results;
    }


    public void batchData(List<Map<String, Map<byte[], byte[]>>> dataList, long expire) {
        redisTemplate.executePipelined(new RedisCallback<List<Map<String, Map<byte[], byte[]>>>>() {
            @Override
            public List<Map<String, Map<byte[], byte[]>>> doInRedis(RedisConnection connection) throws DataAccessException {
                for (Map<String, Map<byte[], byte[]>> map : dataList) {
                    if(null != map) {
                        for (Map.Entry<String, Map<byte[], byte[]>> entry : map.entrySet()) {
                            connection.hMSet(entry.getKey().getBytes(), entry.getValue());
                        }
                    }
                }
                return null;
            }
        });

        // 设置过期时间
        redisTemplate.executePipelined(new RedisCallback<List<Map<String, Map<byte[], byte[]>>>>() {
            @Override
            public List<Map<String, Map<byte[], byte[]>>> doInRedis(RedisConnection connection) throws DataAccessException {
                for (Map<String, Map<byte[], byte[]>> map : dataList) {
                    for (Map.Entry<String, Map<byte[], byte[]>> entry : map.entrySet()) {
                        connection.expire(entry.getKey().getBytes(), expire);
                    }
                }
                return null;
            }
        });
    }

    /**
     * 持久批量插入
     * @param dataList
     */
    public void batchData(List<Map<String, Map<byte[], byte[]>>> dataList) {
        redisTemplate.executePipelined(new RedisCallback<List<Map<String, Map<byte[], byte[]>>>>() {
            @Override
            public List<Map<String, Map<byte[], byte[]>>> doInRedis(RedisConnection connection) throws DataAccessException {
                for (Map<String, Map<byte[], byte[]>> map : dataList) {
                    if(null != map) {
                        for (Map.Entry<String, Map<byte[], byte[]>> entry : map.entrySet()) {
                            connection.hMSet(entry.getKey().getBytes(), entry.getValue());
                        }
                    }
                }
                return null;
            }
        });

        // 设置过期时间
        redisTemplate.executePipelined(new RedisCallback<List<Map<String, Map<byte[], byte[]>>>>() {
            @Override
            public List<Map<String, Map<byte[], byte[]>>> doInRedis(RedisConnection connection) throws DataAccessException {
                for (Map<String, Map<byte[], byte[]>> map : dataList) {
                    for (Map.Entry<String, Map<byte[], byte[]>> entry : map.entrySet()) {
                        connection.persist(entry.getKey().getBytes());
                    }
                }
                return null;
            }
        });
    }

}
